#include <iostream>
#include <atomic>
#include <thread>
#include <cassert>
#include <memory>

/*
    避免竞争条件除了加锁, 还有可以通过使用原子操作的同步特性
    在两个线程的访问之间强加一个顺序就可以数据竞争和未定义行为
    原子操作是一个不可分割的操作, 它要么做完了, 要么没做完, 如果
    修改一个值是原子的, 那么这个值要么是初始值, 要么是修改后的值
    不会处于其它状态, 而cpp中大多数情况下需要通过原子类型来得到
    原子操作
*/


class spinlock_mutex
{
private:
    std::atomic_flag flag;

public:
    // ATOMIC_FLAG_INIT 进行初始化, 默认为 false
    spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}

    void lock() {
        // test_and_set 原子地设置标志位 true 并获得其先前值
        while (flag.test_and_set(std::memory_order_acquire));
    }

    void unlock() {
        flag.clear(std::memory_order_release);
    }
};

spinlock_mutex sm;

void test_print(const char* msg) {
    sm.lock();
    std::cout << std::this_thread::get_id() << " ";
    std::cout << msg << std::endl;
    sm.unlock();
}


int main(int argc, char* argv[]) {
    // atomic_flag 是最简单的标准原子类型, 它代表一个布尔标志
    // 允许的操作有设置和清除, 不同于所有 std::atomic 的特化，它保证是免锁的
    // 常用来实现自旋锁(未获取锁的线程轮询标志, 适用于短时间任务, 可以避免线程阻塞的开销)
    std::thread t1(test_print, "t1");
    std::thread t2(test_print, "t2");
    t1.join();
    t2.join();

    // atomic_flag 不支持简单的无修改查询操作, 因此可以使用
    // std::atomic<bool>, 它仍然是不可拷贝构造和拷贝赋值的
    // 但可以从一个非原子的 bool 来构造它, 因此它可以被初始化为 true/false
    std::atomic<bool> b(true);
    b = false;
    
    // std::atomic<T> 实现不保证是无锁的, 可以通过调用 is_lock_free 来查看
    if (b.is_lock_free()) {
        std::cout << "std::atomic<bool> is lock free" << std::endl;
    }

    // std::atomic<T> 类的写操作是通过 store() 来完成的, 读操作是通过 load()完成的
    // exchange() 可以原子地替换对象的值并获得它先前持有的值
    bool x = b.load(std::memory_order_acquire); // x = false
    b.store(true);  // b = true
    x = b.exchange(false, std::memory_order_acq_rel);   // x = true, b = false

    // std::atomic<T> 还支持比较/交换操作, 它原子地比较原子对象与非原子参数
    // 的值, 若相等则进行交换, 若不相等则进行加载, 常用于用作无锁数据结构的基本模块
    // 比较/交换操作主要通过 compare_exchange_weak 和 compare_exchange_strong 实现
    // 它们接受两个参数 expected 和 desired, 第一个参数通过引用传递
    // compare_exchange_weak 可能会伪失败, 它通常必须用在循环中
    bool expected = true;
    b.compare_exchange_strong(expected, false);
    std::cout << b.load() << '-' << expected << std::endl;
    // if b == expected, b = desired return true
    // else expected = desired return false

    // 除了 std::atomic<T> 外, 标准还支持 std::atomic<T*>, 不过相应的指针
    // 既不能拷贝构造, 也不能拷贝复制, 提供的新操作是指针算术操作, 由 fetch_add 和 fetch_sub
    // 成员函数提供, 可以在所存储的地址上进行原子加减法, 还通过运算符重载
    // 进行了相应的封装
    class Foo{};
    Foo some_array[5];
    std::atomic<Foo*> p(some_array);
    Foo* t = p.fetch_add(2);    // 将 p 加 2 并返回原来的值
    assert(t == some_array);
    assert(p.load() == &some_array[2]);
    t = (p -= 1);
    assert(t == &some_array[1]);
    assert(p.load() == &some_array[1]);

    // 各种原子类型上的所有操作还有等效的非成员函数, 对于大部分非成员函数
    // 来说, 都是以相应的成员函数来命名的, 只是带有一个 atomic_ 前缀, 需要通过
    // 指针的方式传入值
    std::atomic_fetch_add(&p, 3);
    assert(p.load() == &some_array[4]);
    std::atomic_load(&b);
    assert(b.load() == false);

    // c++ 标准库还提供了以原子行为访问 std::shared_ptr<> 实例的自由函数
    // 可用的操作有 load、store、exchange 和 compare/exchange
    struct complex {
        complex() = default;
        complex(int x_, int y_) : x(x_), y(y_) {}
        int x, y;
    };

    std::shared_ptr<complex> c(new complex(1, 2));
    std::atomic<complex> pc;

    if (pc.is_lock_free()) {
        std::cout << "complex is lock free" << std::endl;
    }

    auto ptr = std::atomic_load(&c);
    std::cout << ptr->x << '-' << ptr->y << std::endl;
    std::shared_ptr<complex> nc(new complex(2, 3));
    std::atomic_store(&c, nc);
    ptr = std::atomic_load(&c);
    std::cout << ptr->x << '-' << ptr->y << std::endl;

    return 0;
}